using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace GridSystem
{
    /// <summary>
    /// 【核心组件】附加在Prefab上，定义其作为可放置物体的所有属性和规则。
    /// </summary>
    public class PlaceableObject : MonoBehaviour
    {
        [Header("基本信息")]
        public string itemID;

        [Header("放置规则")]
        [Tooltip("该物体可以被放置在哪些标签的GridSurface上。")]
        public string[] placementSurfaceTags = { "DefaultFloor" };

        [Tooltip("此物体基础占用形状的单元格大小。必须与目标GridSurface的CellSize一致才能放置。")]
        public int cellSize = 1;

        [Tooltip("物体放置时，基于其轴心点的额外局部高度偏移。会与GridSurface的偏移叠加。")]
        public float yOffset = 0f;

        [Tooltip("物体在默认朝向下的2D占用形状（局部坐标）。由自定义编辑器进行可视化编辑。如果对应的标签的GridSurface没有设置占用形状，则该物体将不能被放置。同理在放置的时候，物体会贴合对应标签的GridSurface的占用形状，包括它的朝向。")]
        public List<Vector2Int> definedCells = new() { Vector2Int.zero };

        // 运行时数据，记录当前占用的格子，方便移除时释放
        [System.NonSerialized] public GridSurface OccupiedSurface;
        [System.NonSerialized] public List<Vector2Int> OccupiedCells;

#if UNITY_EDITOR
        [Header("编辑器可视化")]
        [Tooltip("在Scene视图中显示占用形状的可视化。")]
        public bool showVisualization = true;
        [Tooltip("常规填充颜色")]
        public Color fillColor = new(0, 0.5f, 1, 0.35f);
        [Tooltip("常规边框颜色")]
        public Color edgeColor = new(0, 0.5f, 1, 1f);

        [Header("放置预览颜色")]
        [Tooltip("可放置-填充颜色")]
        public Color validFillColor = new(0, 1, 0, 0.35f);
        [Tooltip("可放置-边框颜色")]
        public Color validEdgeColor = new(0, 1, 0, 1f);
        [Tooltip("不可放置-填充颜色")]
        public Color invalidFillColor = new(1, 0, 0, 0.35f);
        [Tooltip("不可放置-边框颜色")]
        public Color invalidEdgeColor = new(1, 0, 0, 1f);

        [Tooltip("边框厚度 (0-0.5)")]
        [Range(0, 0.5f)]
        public float edgeSize = 0.05f;
        [Tooltip("中心锚点的颜色。")]
        public Color anchorColor = Color.red;
#endif

        #region 核心逻辑计算
        /// <summary>
        /// 计算出当前物体实际占用的逻辑网格单元（经过吸附、旋转后的逻辑占位）。
        /// </summary>
        /// <param name="cells"></param>
        /// <param name="surface"></param>
        /// <param name="worldPosition"></param>
        /// <param name="worldRotation"></param>
        public void CalculateWorldCells(List<Vector2Int> cells, GridSurface surface, Vector3 worldPosition, Quaternion worldRotation)
        {
            //下面的CalculateWorldCells方法其实存在bug，这个是算出实际占用的逻辑单元网格。下面的方法只是对每个网格做一个变换，其实是有问题的。因为一个单元格经过旋转后，实际上可能会占用2个格子，甚至3个，应该计算出每个单元格实际占用的surface单元格。
            //这个方法不仅要修复这个bug，还要进行优化，直接存到cells中，不要每次都重新计算，这样效率会高很多

        }
        /// <summary>
        /// 【核心逻辑】计算当此物体以指定姿态放置在目标表面时，其实际占用的逻辑网格单元列表。
        /// 这是实现网格吸附、旋转后占位判断等高级功能的技术基石。
        /// </summary>
        /// <param name="surface">目标放置表面</param>
        /// <param name="worldPosition">物体在世界空间中的目标位置（轴心点）</param>
        /// <param name="worldRotation">物体在世界空间中的目标朝向</param>
        /// <returns>在目标表面坐标系下的、被占用的所有网格单元的列表</returns>
        public List<Vector2Int> CalculateWorldCells(GridSurface surface, Vector3 worldPosition, Quaternion worldRotation)
        {
            // 使用HashSet来存储结果，可以自动处理因旋转导致的重复网格单元
            HashSet<Vector2Int> occupiedCells = new HashSet<Vector2Int>();

            // 遍历此物体定义的基础占用形状 (baseFootprint)
            foreach (Vector2Int localFootprintCell in definedCells)
            {
                // 1. 将2D局部占用坐标转换为3D局部空间坐标（在XZ平面上）
                //    并根据此物体的 cellSize 进行缩放，同时应用物体自身的高度偏移。
                //    这个yOffset是物体自身的局部偏移，还未应用到GridSurface上。
                Vector3 localCellPosition = new Vector3(localFootprintCell.x * cellSize, yOffset, localFootprintCell.y * cellSize);

                // 2. 将局部单元格坐标应用上物体的目标世界旋转，得到一个相对世界原点的偏移向量
                Vector3 rotatedOffset = worldRotation * localCellPosition;

                // 3. 将偏移向量应用到物体的目标世界位置，得到该单元格中心最终的世界坐标
                Vector3 worldCellPosition = worldPosition + rotatedOffset;

                // 4. 将该单元格的世界坐标，转换为其所在的GridSurface的局部网格坐标
                Vector2Int surfaceGridCell = surface.WorldToLocal(worldCellPosition);

                // 5. 将计算出的表面网格坐标添加到结果集中
                occupiedCells.Add(surfaceGridCell);
            }

            // 将HashSet转换为List后返回
            return occupiedCells.ToList();
        }
        #endregion

        #region 核心坐标转换方法
        /// <summary>
        /// 将局部网格整数坐标转换为精确的世界坐标。
        /// 这是【网格朝向自由化】的技术基石。
        /// </summary>
        /// <param name="localPosition">局部整数坐标</param>
        /// <returns>对应的世界坐标</returns>
        public Vector3 LocalToWorld(Vector2Int localPosition)
        {
            // 将整数坐标根据cellSize转换为局部浮点坐标，并应用Y轴偏移
            Vector3 localFloatPos = new Vector3(localPosition.x * cellSize, yOffset, localPosition.y * cellSize);
            // 使用TransformPoint将局部坐标转换为世界坐标
            return transform.TransformPoint(localFloatPos);
        }

        /// <summary>
        /// 将世界坐标转换为最近的局部网格整数坐标。
        /// 同样是【网格朝向自由化】的关键实现。
        /// </summary>
        /// <param name="worldPosition">世界坐标</param>
        /// <returns>最近的局部整数坐标</returns>
        public Vector2Int WorldToLocal(Vector3 worldPosition)
        {
            // 使用InverseTransformPoint将世界坐标转换为局部坐标
            Vector3 localPos = transform.InverseTransformPoint(worldPosition);
            // 根据cellSize计算并四舍五入到最近的整数坐标
            // Y轴的偏移不影响XZ平面的计算，所以这里无需改动
            int x = Mathf.RoundToInt(localPos.x / cellSize);
            int z = Mathf.RoundToInt(localPos.z / cellSize);
            return new Vector2Int(x, z);
        }
        #endregion
    }
}